#include "flashFs.h"
SPIFlash *flash;
spiffs fs;
spiffs_config cfg;
u8_t FS_Work_Buf[256 * 2], FS_FDS[256], FS_Cache_Buf[(256 + 32) * 4];
extern "C" void core_debug(const char *format, ...);
extern "C"
{
    static s32_t _spiffs_erase(spiffs_t *ffs, u32_t addr, u32_t len)
    {
        uint32_t i = 0;
        uint32_t erase_count = (len + 4096 - 1) / 4096;
        for (i = 0; i < erase_count; i++)
        {
            flash->blockErase4K(addr + i * 4096);
        }
        return 0;
    }

    static s32_t _spiffs_read(spiffs_t *ffs, u32_t addr, u32_t size, u8_t *dst)
    {

        flash->readBytes(addr, dst, size);
        return 0;
    }

    static s32_t _spiffs_write(spiffs_t *ffs, u32_t addr, u32_t size, u8_t *dst)
    {
        flash->writeBytes(addr, dst, size);
        return 0;
    }
}
#ifdef ARDUINO
void FlashFs::begin(SPIClass *spii, uint8_t cs)
#else
void FlashFs::begin(SPI_HandleTypeDef *spii, GPIO_TypeDef *port, uint16_t cs)
#endif
{
    this->spi = spii;
#ifdef ARDUINO
    flash = new SPIFlash(spii, cs);
#else
    flash = new SPIFlash(spii, port, cs);
#endif
    if (!flash->initialize())
    {
        while (1)
            ;
    }
    cfg.phys_size = flash->capacity();
    cfg.hal_erase_f = _spiffs_erase;
    cfg.hal_read_f = _spiffs_read;
    cfg.hal_write_f = _spiffs_write;
    cfg.log_block_size = flash->blockSize();
    cfg.log_page_size = 256;
    cfg.phys_erase_block = 4096;
    mount();
}
void FlashFs::mount()
{
    int res;
    if ((res = SPIFFS_mount(&fs, &cfg, FS_Work_Buf, FS_FDS, sizeof(FS_FDS), FS_Cache_Buf, sizeof(FS_Cache_Buf), NULL)) == SPIFFS_OK && SPIFFS_errno(&fs) == SPIFFS_OK)
    {
        core_debug("SPIFFS mounted\n");
    }
    else
    {
        core_debug("SPIFFS mount failed: %d\n", SPIFFS_errno(&fs));
        format();
    }
}
void FlashFs::format()
{
    SPIFFS_unmount(&fs);
    core_debug("formatting spiffs...\n");
    if (SPIFFS_format(&fs) != SPIFFS_OK)
    {
        core_debug("SPIFFS format failed: %d\n", SPIFFS_errno(&fs));
    }
    mount();
}
u32_t FlashFs::flash_size()
{
    return flash->capacity();
}
u32_t FlashFs::flash_chip_code()
{
    return flash->_jedecID;
}
int FlashFs::info()
{
    return SPIFFS_info(&fs, &total, &used_space);
}
void FlashFs::dir()
{
    spiffs_DIR d;
    struct spiffs_dirent e;
    struct spiffs_dirent *pe = &e;

    SPIFFS_opendir(&fs, "/", &d);
    while ((pe = SPIFFS_readdir(&d, pe)))
    {
        core_debug("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
    }
    SPIFFS_closedir(&d);
}
FlashFile::FlashFile() {}
int FlashFile::open_rdonly(const char *tfilename)
{
    this->filename = tfilename;
    fd = SPIFFS_open(&fs, tfilename, SPIFFS_O_RDONLY, 0);
    return fd;
}
int FlashFile::open_write(const char *tfilename)
{
    this->filename = tfilename;
    fd = SPIFFS_open(&fs, tfilename, SPIFFS_RDWR | SPIFFS_O_CREAT | SPIFFS_O_TRUNC, 0);
    return fd;
}
int FlashFile::write(const char *buf, int len)
{
    int r = SPIFFS_write(&fs, fd, (void *)buf, len);
    if (r < 0)
        core_debug("errno %d\n", SPIFFS_errno(&fs));
    return r;
}
int FlashFile::read(char *buf, int len)
{
    int r = SPIFFS_read(&fs, fd, buf, len);
    if (r < 0)
        core_debug("errno %d\n", SPIFFS_errno(&fs));
    return r;
}
int FlashFile::close()
{
    return SPIFFS_close(&fs, fd);
}
int FlashFile::remove()
{
    return SPIFFS_remove(&fs, filename);
};
int FlashFile::size()
{
    spiffs_stat fdd;
    int res = SPIFFS_stat(&fs, filename, &fdd);
    if (res == SPIFFS_OK)
    {
        return fdd.size;
    }
    return res;
};
int FlashFile::stat(const char *filename)
{
    spiffs_stat fdd;
    SPIFFS_stat(&fs, filename, &fdd);
    return fdd.size;
}
int FlashFile::eof()
{
    return SPIFFS_eof(&fs, fd);
};
int FlashFile::fseek(int offs, int whence)
{
    return SPIFFS_lseek(&fs, fd, offs, whence);
};
int FlashFile::rewind()
{
    return 0;
};

int flash_rename(const char *src, const char *dest)
{
    return SPIFFS_rename(&fs, src, dest);
}
int FlashFile::fflush()
{
    return SPIFFS_fflush(&fs, fd);
}
